/*
 * Copyright 2009 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.google.android.apps.mytracks.services.sensors.ant;

import static com.google.android.apps.mytracks.Constants.TAG;

import com.dsi.ant.AntDefine;
import com.dsi.ant.AntMesg;
import com.dsi.ant.exception.AntInterfaceException;
import com.google.android.apps.mytracks.content.Sensor;

import android.content.Context;
import android.util.Log;

import java.util.HashMap;
import java.util.Map;

/**
 * A sensor manager to connect ANT+ sensors.
 * This can include heart rate monitors.
 *
 * @author Sandor Dornbush
 */
public class AntDirectSensorManager extends AbstractAntSensorManager {
  private final Map<Byte, AntSensorHandler> sensorHandlers;

  AntDirectSensorManager(Context context, AntSensorHandler sensorHandler) {
    super(context);

    sensorHandlers = new HashMap<Byte, AntSensorHandler>();
    sensorHandlers.put(sensorHandler.getChannelNumber(), sensorHandler);

    for (AntSensorHandler handler : sensorHandlers.values()) {
      Log.i(TAG, String.format("Using sensor %s channel %d device number %d",
          handler.getSensorType(), handler.getChannelNumber(), handler.getDeviceNumber()));
    }
  }

  @Override
  protected void handleOneMessage(byte channel, byte messageId, byte[] messageData) {
    AntSensorHandler sensorHandler = sensorHandlers.get(channel);
    if (sensorHandler == null) {
      Log.d(TAG, "Unhandled message on channel " + channel);
      return;
    }

    switch (messageId) {
      case AntMesg.MESG_BROADCAST_DATA_ID:
        handleBroadcastData(sensorHandler, messageData);
        break;
      case AntMesg.MESG_RESPONSE_EVENT_ID:
        handleMessageResponse(messageData);
        break;
      case AntMesg.MESG_CHANNEL_ID_ID:
        handleChannelId(sensorHandler, messageData);
        break;
      default:
        Log.e(TAG, "Unexpected message id: " + messageId);
    }
  }

  private void handleBroadcastData(AntSensorHandler sensorHandler, byte[] antMessage) {
    if (sensorHandler.getDeviceNumber() == WILDCARD) {
      try {
        getAntReceiver().ANTRequestMessage(sensorHandler.getChannelNumber(),
            AntMesg.MESG_CHANNEL_ID_ID);
      } catch (AntInterfaceException e) {
        Log.e(TAG, "ANT error handling broadcast data", e);
      }
      Log.d(TAG, "Requesting channel id id.");
    }

    setSensorState(Sensor.SensorState.CONNECTED);

    sensorData = Sensor.SensorDataSet.newBuilder()
        .mergeFrom(sensorHandler.handleBroadcastData(antMessage))
        .setCreationTime(System.currentTimeMillis())
        .build();
  }

  void handleChannelId(AntSensorHandler sensorHandler, byte[] rawMessage) {
    AntChannelIdMessage message = new AntChannelIdMessage(rawMessage);
    Log.i(TAG, "Found device id: " + message.getDeviceNumber());

    sensorHandler.setDeviceNumber(message.getDeviceNumber());
  }

  private void handleMessageResponse(byte[] rawMessage) {
    AntChannelResponseMessage message = new AntChannelResponseMessage(rawMessage);
    switch (message.getMessageId()) {
      case AntMesg.MESG_EVENT_ID:
        if (message.getMessageCode() == AntDefine.EVENT_RX_SEARCH_TIMEOUT) {
          // Search timeout
          Log.w(TAG, "Search timed out. Unassigning channel.");
          try {
            getAntReceiver().ANTUnassignChannel((byte) 0);
          } catch (AntInterfaceException e) {
            Log.e(TAG, "ANT error unassigning channel", e);
          }
          setSensorState(Sensor.SensorState.DISCONNECTED);
        }
        break;

      case AntMesg.MESG_UNASSIGN_CHANNEL_ID:
        setSensorState(Sensor.SensorState.DISCONNECTED);
        Log.i(TAG, "Disconnected from the sensor: " + getSensorState());
        break;
    }
  }

  @Override protected void setupAntSensorChannels() {
    for (AntSensorHandler sensorHandler : sensorHandlers.values()) {
      setupAntSensorHandler(sensorHandler);
    }
  }

  private void setupAntSensorHandler(AntSensorHandler sensorHandler) {
    setupAntSensorChannel(
        sensorHandler.getChannelNumber(),
        sensorHandler.getDeviceNumber(),
        sensorHandler.getSensorType().getDeviceType());
  }
}
